#include "teleport.h"
//#include <boost/regex>

void (*tPrint)(int r,const char *text,...) = (void(*)(int,const char*,...))0x004b3800;

float Dist3D(D3DXVECTOR3 a, D3DXVECTOR3 b)
{
	return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y) + (a.z-b.z)*(a.z-b.z));
}


string LocList::ClosestLoc(D3DXVECTOR3 coord){
	string closest;
	string MapName = Server()->GetMapName();
	float close = 9.0e9f;
	map<string, D3DXVECTOR3>* tele_locs = &locs_per_map[MapName];
	for(map<string,D3DXVECTOR3>::iterator loc = tele_locs->begin(); loc!= tele_locs->end(); loc++)
	{
		if(Dist3D(coord, loc->second) < close)
		{
			closest = loc->first;
			close = Dist3D(coord, loc->second);
		}
	}
	return closest;
}

//---------------------------------------------------------------------------
vect3 StrToCoord(string str)
{
	float x = 0, y = 0, z = 0;
	if(sscanf_s(str.c_str(), "%f, %f, %f", &x, &y, &z) != 3)
		if(sscanf_s(str.c_str(), "%f,%f,%f", &x, &y, &z) !=3)
			if(sscanf_s(str.c_str(), "%f %f %f", &x, &y, &z) !=3)
				return BAD_LOC;
	//tPrint(0,"X: %f, Y: %f, Z: %f", x, y, z);
	return vect3(x, y, z);
}

string CoordToStr(vect3 coord)
{
	   stringstream ss;
	   ss.precision(3);
	   ss << coord.x << ", " << coord.y << ", " << coord.z;
	   return ss.str();
}
void LocList::Save()
{
	string filename = Manager()->save_dir + "locations.ini";
	if(!CIniFile::Create(filename))
	{
		tPrint(0, "Tele Locations file creation failed. (Perhaps you didn't make the directories yet?)");
		return;
	}
	// map< MAPNAME,  map< LOCNAME, COORD>  >

	for(map<string,map<string, vect3>>::iterator maplist = locs_per_map.begin(); maplist != locs_per_map.end(); maplist++)
	{
		if(Server()->debug)
			tPrint(0, "Saving for map: %s", maplist->first.c_str());
		CIniFile::AddSection(maplist->first, filename);
		for(map<string,D3DXVECTOR3>::iterator loc = maplist->second.begin(); loc != maplist->second.end(); loc++)
		{
			if(Server()->debug)
				tPrint(0, "Adding Location %s in map %s", maplist->first.c_str(), loc->first.c_str());
			CIniFile::SetValue(loc->first, CoordToStr(loc->second), maplist->first, filename);
		}
	}
}
void LocList::Load()
{
	string filename = Manager()->save_dir + "locations.ini";
	
	vector<CIniFile::Record> info;
	if(CIniFile::Load(filename, info))
	{
		for(size_t i=0; i<info.size(); i++)
		{
			locs_per_map[info[i].Section][info[i].Key] = StrToCoord(info[i].Value);
		}
		tPrint(0, "Loaded tele locations.");
	} else
		tPrint(0, "Error in loading tele locations.");
}
D3DXVECTOR3 LocList::GetLoc(string loc)
{
	// A: check if it's a coord and not a location.
	vect3 coord;
	if((coord=StrToCoord(loc)) != BAD_LOC)
		return coord;


	string MapName = Server()->GetMapName();
	map<string, D3DXVECTOR3>* tele_locs = &locs_per_map[MapName];
	map<string, D3DXVECTOR3>::iterator location = tele_locs->find(loc);

	if(location != tele_locs->end())
		return location->second;

	location = default_locs.find(loc);

	if(location != default_locs.end())
		return location->second;

	return BAD_LOC; // aka Infinity
}
//---------------------------------------------------------------------------

void LocList::AddLoc(string loc, D3DXVECTOR3 coord)
{
	string MapName = Server()->GetMapName();
	map<string, D3DXVECTOR3>* tele_locs = &locs_per_map[MapName];
	(*tele_locs)[loc] = coord;
}

toggle Jump(ArgContainer cmd)
{	// jump <player> <coord>
	if(cmd.argc<3)
		return CMDFAIL;
	vect3 coord = Manager()->teleport.GetLoc(cmd[2] + " " + cmd[3] + " " + cmd[4]);
	if(coord == BAD_LOC)
		return CMDFAIL;

	vector<playerindex> players = StrToIndList(cmd[1]);
	for(vector<playerindex>::iterator pl=players.begin(); pl!=players.end(); pl++)
	{
		playerindex pl_ind=*pl;
		if (pl_ind == -1)
			return CMDFAIL;	
		if(Server()->IsSpawned(pl_ind))
		{
			float yaw = Server()->GetPlayer(pl_ind)->Rotation.x;
			vect3 coord2= Server()->GetCoord(pl_ind);
			coord2.x += coord.y * cos(yaw) + coord.x * sin(yaw);
			coord2.y += coord.y * sin(yaw) - coord.x * cos(yaw);
			coord2.z += coord.z;

			Server()->SetCoord(pl_ind, coord2);
		}
	}
	return CMDSUCC;
}

toggle TelRelative(ArgContainer cmd)
{	// jump <player> <coord>
	if(cmd.argc<3)
		return CMDFAIL;
	vect3 coord = Manager()->teleport.GetLoc(cmd[2] + " " + cmd[3] + " " + cmd[4]);
	if(coord == BAD_LOC)
		return CMDFAIL;

	vector<playerindex> players = StrToIndList(cmd[1]);
	for(vector<playerindex>::iterator pl=players.begin(); pl!=players.end(); pl++)
	{
		playerindex pl_ind=*pl;
		if (pl_ind == -1)
			return CMDFAIL;	
		if(Server()->IsSpawned(pl_ind))
			Server()->SetCoord(pl_ind, Server()->GetCoord(pl_ind) + coord);
	}
	return CMDSUCC;
}

toggle Teleport(ArgContainer cmd)
{	// teleport, <player>, loc
	// teleport, <player>, x, y, z
	if(cmd.argc<3)
		return CMDFAIL;
	D3DXVECTOR3 acoord = BAD_LOC;
	string coord = cmd[2];
	if(cmd.argc == 5)
		coord = cmd[2] + " " + cmd[3] + " " + cmd[4];

	acoord = Manager()->teleport.GetLoc(coord);
	
	if(acoord == BAD_LOC)
	{
		tPrint(0, "Bad Location.");
		return CMDFAIL;
	}

	vector<playerindex> players = StrToIndList(cmd[1]);
	for(vector<playerindex>::iterator pl=players.begin(); pl!=players.end(); pl++)
	{
		playerindex pl_ind=*pl;
		if (pl_ind == -1)
			return CMDFAIL;	
		Server()->SetCoord(pl_ind, acoord);
		tPrint(0, "Player %d teleported to %s", pl_ind, cmd[2].c_str());
	}
	return CMDSUCC;
}

toggle AddTel(ArgContainer cmd)
{// addtel <location> (<player> | <coord>)
	if(cmd.argc<3)
		return CMDFAIL;

	D3DXVECTOR3 acoord;

	if(cmd.argc == 5)
	{
		acoord = StrToCoord(cmd[2] + " " + cmd[3] + " " + cmd[4]);
	}else
	{
		if((acoord = StrToCoord(cmd[2])) == BAD_LOC)
		{
			vector<playerindex> players = StrToIndList(cmd[2]);
			if(players.size() != 1)
				return CMDFAIL;
			acoord = Server()->GetCoord(players[0]);
		}
	}
	if(acoord == BAD_LOC)
	{
		tPrint(0, "Bad Location.");
		return CMDFAIL;
	}
	Manager()->teleport.AddLoc(cmd[1], acoord);
	tPrint(0, "Loc %s added.", cmd[1].c_str());
	
	return CMDSUCC;
}
//toggle SaveTel(ArgContainer cmd)
//{
//	Manager()->teleport.Save();
//	return CMDSUCC;
//}
//toggle LoadTel(ArgContainer cmd)
//{
//	Manager()->teleport.Load();
//	return CMDSUCC;
//}